#include "ds18b20.h"

static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t len, loff_t *pos);

static int ds18b20_probe(struct platform_device *pdev);
static int ds18b20_remove(struct platform_device *pdev);

//设备号
static dev_t ds18b20_num;
//cdev对象
static struct cdev ds18b20_cdev;
//class对象
static struct class *ds18b20_class;
//操作函数
static struct file_operations ds18b20_ops = {
	.owner = THIS_MODULE,
	.read = ds18b20_read,
};
//ds18b20资源
static struct ds18b20_handle ds18b20_handle[DS18B20_NUMBER];

/* 匹配列表，用于设备树和平台驱动匹配 */
static const struct of_device_id ds18b20_of_match[] = {
	{.compatible = "atk,ds18b20"},
	{ /* Sentinel */ }
};
//平台驱动
static struct platform_driver ds18b20_drv = {
	.driver = {
		.name = "ds18b20",
		.owner = THIS_MODULE,
		.pm = NULL,
		.of_match_table = ds18b20_of_match,
	},
	.probe = ds18b20_probe,
	.remove = ds18b20_remove,
};

static int ds18b20_start(struct ds18b20_handle *ds18b20)
{
	int state;
	unsigned long irq_state;

	//设置为输出，并拉高1ms，使总线进入空闲状态
	gpio_direction_output(ds18b20->gpio, 1);
	msleep_interruptible(2);

	//关闭本核中断，目的是为了得到精确的延时
	local_irq_save(irq_state);

	//拉低总线480~960us,然后再拉高15~60us，输出开始信号
	gpio_set_value(ds18b20->gpio, 0);
	udelay(480);
	gpio_set_value(ds18b20->gpio, 1);
	udelay(15);

	//释放总线
	gpio_direction_input(ds18b20->gpio);
	//延时，确保响应输出
	udelay(45);
	//读取响应状态
	state = gpio_get_value(ds18b20->gpio);

	//恢复本核中断
	local_irq_restore(irq_state);

	//确保响应结束
	msleep_interruptible(2);

	if((state != 0) || (gpio_get_value(ds18b20->gpio) == 0))
		return -EAGAIN;
	return 0;
}

static void ds18b20_write_bit(struct ds18b20_handle *ds18b20, int value)
{
	unsigned long irq_state;

	//关闭本核中断，目的是为了得到精确的延时
	local_irq_save(irq_state);

	if(value)
	{
		//输出1，先拉低1~15us，然后拉高，整个写周期至少为60us即可
		gpio_direction_output(ds18b20->gpio, 0);
		udelay(1);
		gpio_set_value(ds18b20->gpio, 1);
		udelay(60);
	}
	else
	{
		//输出0，拉低至少60~120us
		gpio_direction_output(ds18b20->gpio, 0);
		udelay(60);
		gpio_set_value(ds18b20->gpio, 1);
	}

	//恢复本核中断
	local_irq_restore(irq_state);

	//两次写操作至少间隔1us
	udelay(1);
}


static int ds18b20_read_bit(struct ds18b20_handle *ds18b20)
{
	int result;
	unsigned long irq_state;

	//关闭本核中断，目的是为了得到精确的延时
	local_irq_save(irq_state);

	//先拉低1~15us，释放总线，然后延时15us，18b20输出数据，整个过程大于60us
	gpio_direction_output(ds18b20->gpio, 0);
	udelay(1);
	gpio_direction_input(ds18b20->gpio);
	udelay(15);
	result = gpio_get_value(ds18b20->gpio);
	udelay(45);

	//恢复本核中断
	local_irq_restore(irq_state);

	//两次读操作至少间隔1us
	udelay(1);

	return result;
}

static void ds18b20_write_byte(struct ds18b20_handle *ds18b20, uint8_t value)
{
	int i;

	for(i=0; i<8; i++) 
	{
		if(value & (0x01 << i))
			ds18b20_write_bit(ds18b20, 1);
		else
			ds18b20_write_bit(ds18b20, 0);
	}
}

static uint8_t ds18b20_read_byte(struct ds18b20_handle *ds18b20)
{
	uint8_t value;
	int i;

	value = 0;
	for(i=0; i<8; i++) 
	{
		if(ds18b20_read_bit(ds18b20))
			value |= 0x01 << i;
	}

	return value;
}

static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t len, loff_t *pos)
{
	uint32_t id;
	long result;
	uint8_t temp[2];

	id = MINOR(file->f_inode->i_rdev);
	if((id > DS18B20_NUMBER) || (len < sizeof(int16_t)))
		return -EINVAL;

	//产生开始信号
	result = ds18b20_start(&ds18b20_handle[id]);
	if(result < 0)
		return result;
	//发送0xcc，忽略 64 位 ROM 地址
	ds18b20_write_byte(&ds18b20_handle[id], 0xcc);
	//发送0x44，启动温度转换
	ds18b20_write_byte(&ds18b20_handle[id], 0x44);

	//延时800ms，等待转换完成
	msleep_interruptible(800);

	//产生开始信号
	result = ds18b20_start(&ds18b20_handle[id]);
	if(result < 0)
		return result;
	//发送0xcc，忽略 64 位 ROM 地址
	ds18b20_write_byte(&ds18b20_handle[id], 0xcc);
	//发送0xBE，连续读内部 RAM 的 9 字节的内容
	ds18b20_write_byte(&ds18b20_handle[id], 0xbe);
	//读取第一个字节
	temp[0] = ds18b20_read_byte(&ds18b20_handle[id]);
	//读取第二个字节
	temp[1] = ds18b20_read_byte(&ds18b20_handle[id]);

	if(copy_to_user(buf, &temp, sizeof(int16_t)))
		return -EFAULT;

	return sizeof(int16_t);
}

//设备和驱动匹配成功执行
static int ds18b20_probe(struct platform_device *pdev)
{
	int result;
	uint32_t id;
	struct device *device;

	printk("%s\r\n", __FUNCTION__);

	//获取设备ID号
	result = of_property_read_u32(pdev->dev.of_node, "id", &id);
	if(result != 0)
	{
		printk("get dev id failed\r\n");
		return result;
	}
	if(id > DS18B20_NUMBER)
	{
		printk("dev id invalid\r\n");
		return -EINVAL;
	}

	//检查是否占用
	if(ds18b20_handle[id].use_flag) 
	{
		printk("dev id repetition\r\n");
		return -EINVAL;
	}
	pdev->id = id;

	//标记为已占用
	ds18b20_handle[id].use_flag = 1;

	//获取GPIO号
	ds18b20_handle[id].gpio = of_get_named_gpio(pdev->dev.of_node, "data_pin", 0);
	if(ds18b20_handle[id].gpio < 0)
	{
		ds18b20_handle[id].use_flag = 0;
		printk("get gpio failed\r\n");
		return ds18b20_handle[id].gpio;
	}
	//申请GPIO
	result = gpio_request(ds18b20_handle[id].gpio, "ds18b20");
	if(result != 0)
	{
		ds18b20_handle[id].use_flag = 0;
		printk("request gpio failed\r\n");
		return result;
	}

	//创建设备文件，将ID作为此设备的次设备号
	device = device_create(ds18b20_class, NULL, ds18b20_num+id, NULL, "ds18b20-%d", id);
	if(IS_ERR(device))
	{
		gpio_free(ds18b20_handle[id].gpio);
		ds18b20_handle[id].use_flag = 0;
		printk("device create failed");
		return PTR_ERR(device);
	}

	return 0;
}

//设备或驱动卸载时执行
static int ds18b20_remove(struct platform_device *pdev)
{
	uint32_t id;

	printk("%s\r\n", __FUNCTION__);

	id = pdev->id;
	if(!ds18b20_handle[id].use_flag)
	{
		printk("verification failed\r\n");
		return -EINVAL;
	}

	device_destroy(ds18b20_class, ds18b20_num+id);
	gpio_free(ds18b20_handle[id].gpio);

	ds18b20_handle[id].use_flag = 0;

	return 0;
}

static int __init ds18b20_drv_init(void)
{
	int i;
	int err = 0;

	printk("%s\r\n", __FUNCTION__);

	for(i=0; i<DS18B20_NUMBER; i++)
	{
		ds18b20_handle[i].use_flag = 0;
	}

	//根据次设备号起始值动态注册字符设备号
	err = alloc_chrdev_region(&ds18b20_num, 0, DS18B20_NUMBER, "atk,ds18b20");
	if(err != 0)
	{
		printk("alloc chrdev failed\r\n");
		return err;
	}
	printk("first device major %d, minor %d\r\n", MAJOR(ds18b20_num), MINOR(ds18b20_num));

	//初始化CDEV对象
	cdev_init(&ds18b20_cdev, &ds18b20_ops);
	//向系统添加CDEV对象
	err = cdev_add(&ds18b20_cdev, ds18b20_num, DS18B20_NUMBER);
	if(err != 0)
	{
		unregister_chrdev_region(ds18b20_num, DS18B20_NUMBER);
		printk("add cdev failed\r\n");
		return err;
	}

	//创建class对象
	ds18b20_class = class_create(THIS_MODULE, "ds18b20,class");
	if(IS_ERR(ds18b20_class))
	{
		cdev_del(&ds18b20_cdev);
		unregister_chrdev_region(ds18b20_num, DS18B20_NUMBER);
		printk("class create failed");
		return PTR_ERR(ds18b20_class);
	}

	//注册平台驱动
	err = platform_driver_register(&ds18b20_drv);
	if(err != 0)
	{
		class_destroy(ds18b20_class);
		cdev_del(&ds18b20_cdev);
		unregister_chrdev_region(ds18b20_num, DS18B20_NUMBER);
		printk("add cdev failed\r\n");
		return err;
	}
	return err;
}

static void __exit ds18b20_drv_exit(void)
{
	printk("%s\r\n", __FUNCTION__);

	//注销平台驱动
	platform_driver_unregister(&ds18b20_drv);
	//销毁class对象
	class_destroy(ds18b20_class);
	//从系统删除CDEV对象
	cdev_del(&ds18b20_cdev);
	//注销字符设备号
	unregister_chrdev_region(ds18b20_num, DS18B20_NUMBER);
}

module_init(ds18b20_drv_init);
module_exit(ds18b20_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LF");
MODULE_DESCRIPTION("ds18b20_dev");
